<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>

</body>
<script src="../underscore1.8.3.js"></script>
<script>
    //【案例】map函数的例子
    var res1 = _.map([1, 2, 3], function(num) {
        return num * 3;
    });
    console.log(res1); //=> [3, 6, 9]

    var res2 = _.map({
        one: 1,
        two: 2,
        three: 3
    }, function(num, key) {
        return num * 3;
    });
    console.log(res2); //=> [3, 6, 9]

    var res3 = _.map([
        [1, 2],
        [3, 4]
    ], _.first);
    console.log(res3); //=> [1, 3]
    //【1】 使用迭代，而不是循环
    //【1-1】在函数式编程，更推荐使用迭代
    var results = _.map([1, 2, 3], function(elem) {
        return console.log(elem * 2);
    }); // => [2,4,6]
    //【1-2】而不是循环：
    var results = [];
    var elems = [1, 2, 3];
    for (var i = 0, length = elems.length; i < length; i++) {
        results.push(elems[i] * 2);
    };
    console.log(results); // => [2,4,6]


    //【2】iteratee【iteratee英文是迭代器的意思】
    // 对于一个迭代来说，他至少由如下两个部分构成：
    // 被迭代集合
    // 当前迭代过程

    // 在 underscore 中，当前迭代过程是一个函数，他被称为 iteratee （直译为被迭代者），他将对当前的迭代元素进行处理。我们看到 _.map 的实现：
    _.map = _.collect = function(obj, iteratee, context) {

        iteratee = cb(iteratee, context);

        var keys = !isArrayLike(obj) && _.keys(obj),
            length = (keys || obj).length,
            results = Array(length);

        // 定长初始化数组
        for (var index = 0; index < length; index++) {
            var currentKey = keys ? keys[index] : index;
            results[index] = iteratee(obj[currentKey], currentKey, obj);
        }
        return results;
    };
    // 我们传递给的 _.map 的第二个参数就是一个iteratee ，他可能是函数，对象，甚至是字符串，underscore 会将其统一处理为一个函数。


    // 这个处理由underscore 的内置函数 cb 来完成。下面我们看一下 cb 的实现：
    var cb = function(value, context, argCount) {
        // 是否用自定义的iteratee
        if (_.iteratee !== builtinIteratee) return _.iteratee(value,
            context);
        // 针对不同的情况
        if (value == null) return _.identity;
        if (_.isFunction(value)) return optimizeCb(value, context, argCount);
        if (_.isObject(value)) return _.matcher(value);
        return _.property(value);
    };
    // cb 将根据不同情况来为我们的迭代创建一个迭代过程 iteratee ，服务于每轮迭代：value 为 null
    // 如果传入的 value 为 null ，亦即没有传入 iteratee ，则 iteratee 的行为只是返回当前迭代元素自身，比如：
    var results = _.map([1, 2, 3]); // => results：[1,2,3]

    //【2-1】如果传入 value 是一个函数，那么通过内置函数 optimizeCb 对其进行优化， optimizeCb 的作用放到之后讲，先来看个传入函数的例
    var results = _.map([1, 2, 3], function(value, index, obj) {
        return '[' + obj + ']' + '\'s ' + index + ' position is ' + value;
    });
    // => results: [
    // "[1,2,3]'s 0 position is 1",
    // "[1,2,3]'s 1 position is 2",
    // "[1,2,3]'s 2 position is 3"
    // ]


    //【2-2】如果 value 传入的是一个对象，那么返回的 iteratee （ _.matcher ）的目的是想要知道当前被迭代元素是否匹配给定的这个对象：
    var results = _.map([{
        name: 'yoyoyohamapi'
    }, {
        name: 'wxj',
        age: 13
    }], {
        name: 'wxj'
    });
    // => results: [false,true]

    //【2-3】如果以上情况都不是， 那么传入的 value 会是一个字面量（直接量），他指示了一个对象的属性 key ，
    // 返回的 iteratee （ _.property ）将用来获得该属性对应的值：
    var results = _.map([{
        name: 'yoyoyohamapi'
    }, {
        name: 'wxj'
    }], 'name');
    // => results: ['yoyoyohamapi', 'wxj'];
</script>

</html>